﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Windows.Forms.Design.Behavior;

namespace System.Windows.Forms.Design;

/// <summary>
///  Designer for ToolStripMenuItems.
/// </summary>
internal class ToolStripMenuItemDesigner : ToolStripDropDownItemDesigner
{
    private const int GLYPHINSET = 2;
    // This is the typeHereNode that appears to the bottom and right of each committed ToolStripDropDownItem
    private DesignerToolStripControlHost _typeHereNode;
    private ToolStripTemplateNode _typeHereTemplateNode;
    // This is the TemplateNode.EditorToolStrip. The editor ToolStrip is encapsulated in a ToolStripControlHost and
    // then added as a ToolStripDropDownItem on the current ToolStripDropDownItems DropDown.
    private DesignerToolStripControlHost _commitedEditorNode;
    // Actual InSitu Editor. This is created for every selected ToolStripDropDownItem and is DISPOSED immediately
    // after the item comes out of InSitu edit Mode.
    private ToolStripTemplateNode _commitedTemplateNode;

    // DesignerHost for current Component
    private IDesignerHost _designerHost;
    private ToolStripItem _parentItem;
    private ToolStripAdornerWindowService _toolStripAdornerWindowService;
    private ToolStripKeyboardHandlingService _keyboardHandlingService;

    // Make Selection service a class level member.. used a lot.
    private ISelectionService _selectionService;

    private IComponentChangeService _componentChangeService;

    // DesignerTransaction used for removing Items
    private DesignerTransaction _pendingTransaction;
    private bool _fireComponentChanged;

    // indicates that we are adding new MenuItem ..
    private bool _componentAddingFired;
    // new item index
    private int _indexToInsertNewItem = -1;
    // only used by DropDownMenu, DropDown or ContextMenuStrip.
    private DesignerTransaction _insertMenuItemTransaction;

    // We want one parent transaction when the item is added by InSitu editing which is now supported only for MenuItems.
    // This parent transaction would include
    // - Adding the DummyItem
    // - Swapping the DummyItem with the InsItu
    // - Committing the new Item with the text entered in the InSitu.
    private DesignerTransaction _newMenuItemTransaction;
    // DropDownToInvalidate
    private Rectangle _dropDownSizeToInvalidate = Rectangle.Empty;
    // DropDownToInvalidate while ComponentRemove
    private Rectangle _boundsToInvalidateOnRemove = Rectangle.Empty;
    private ToolStripDropDownGlyph _rootControlGlyph;
    private bool _initialized;

    // Hook on the Undoing and Undone Events...
    private UndoEngine _undoEngine;
    private bool _undoingCalled;
    private bool _addingDummyItem;

    // custom DropDown
    private ToolStripDropDown _customDropDown;
    private bool _dropDownSet;
    private SerializationStore _serializedDataForDropDownItems;
    private bool _dropDownSetFailed;

    /// <summary>
    ///  The ToolStripDropDownItems are the associated components.
    ///  We want those to come with in any cut, copy opreations.
    /// </summary>
    public override ICollection AssociatedComponents
    {
        get
        {
            ArrayList items = [];
            // Return the list only if the DropDown is AutoGenerated. else the DropDown property set/get will handle it.
            if (MenuItem.DropDown.IsAutoGenerated)
            {
                foreach (ToolStripItem item in MenuItem.DropDownItems)
                {
                    if (item is not DesignerToolStripControlHost)
                    {
                        items.Add(item);
                    }
                }
            }

            return items;
        }
    }

    /// <summary>
    ///  ShadowProperty.
    /// </summary>
    private bool CheckOnClick
    {
        get => (bool)ShadowProperties[nameof(CheckOnClick)];
        set => ShadowProperties[nameof(CheckOnClick)] = value;
    }

    private bool DoubleClickEnabled
    {
        get => (bool)ShadowProperties[nameof(DoubleClickEnabled)];
        set => ShadowProperties[nameof(DoubleClickEnabled)] = value;
    }

    private ToolStripDropDown DropDown
    {
        get => _customDropDown;
        set
        {
            _dropDownSetFailed = false;
            if (IsParentDropDown(value))
            {
                _dropDownSetFailed = true;
                throw new ArgumentException(string.Format(SR.InvalidArgument, "DropDown", value.ToString()));
            }

            // Check if DropDown Exists .. and if yes then Close the DropDown
            if (MenuItem.DropDown is not null)
            {
                RemoveTypeHereNode(MenuItem);

                // remove and destroy all the items...
                if (MenuItem.DropDown.IsAutoGenerated && MenuItem.DropDownItems.Count > 0)
                {
                    // Serialize all the DropDownItems for this Item....
                    if (TryGetService(out ComponentSerializationService serializationService))
                    {
                        _serializedDataForDropDownItems = serializationService.CreateStore();
                        foreach (ToolStripItem item in MenuItem.DropDownItems)
                        {
                            // Don't Serialize the DesignerToolStripControlHost...
                            if (item is not DesignerToolStripControlHost)
                            {
                                serializationService.Serialize(_serializedDataForDropDownItems, item);
                            }
                        }

                        // close the SerializationStore to Serialize Items..
                        _serializedDataForDropDownItems.Close();
                    }

                    ToolStripItem[] items = new ToolStripItem[MenuItem.DropDownItems.Count];
                    MenuItem.DropDownItems.CopyTo(items, 0);
                    foreach (ToolStripItem item in items)
                    {
                        MenuItem.DropDownItems.Remove(item);
                        _designerHost.DestroyComponent(item);
                    }
                }

                MenuItem.HideDropDown();
            }

            // Set The DropDown
            MenuItem.DropDown = value;
            // Set the Shadow Property
            _customDropDown = value;
            // If DropDown is Set to null and we have valid serializedData, then use it to recreate Items.
            if (value is null && !_dropDownSet && _serializedDataForDropDownItems is not null)
            {
                try
                {
                    // turn off Adding/Added events listened to by the ToolStripDesigner...
                    ToolStripDesigner.s_autoAddNewItems = false;
                    CreatetypeHereNode();
                    if (TryGetService(out ComponentSerializationService serializationService))
                    {
                        serializationService.Deserialize(_serializedDataForDropDownItems);
                        _serializedDataForDropDownItems = null;
                    }
                }
                finally
                {
                    // turn on Adding/Added events listened to by the ToolStripDesigner...
                    ToolStripDesigner.s_autoAddNewItems = true;
                }
            }

            MenuItem.DropDown.OwnerItem = MenuItem;
            MenuItem.DropDown.Disposed += OnDropDownDisposed;

            // Show the drop down.
            if (MenuItem.Equals(_selectionService.PrimarySelection))
            {
                // Add TypeHereNode & show the Dropdown
                InitializeDropDown();
            }
        }
    }

    /// <summary>
    ///  ToolStripEditorManager used this internal property to
    ///  Activate the editor.
    /// </summary>
    internal override ToolStripTemplateNode Editor
    {
        get
        {
            if (base.Editor is not null)
            {
                return base.Editor;
            }

            if (_commitedTemplateNode is not null)
            {
                return _commitedTemplateNode;
            }

            return _typeHereTemplateNode;
        }
        set => _commitedTemplateNode = value;
    }

    /// <summary>
    ///  True if the MenuItem is on ContextMenu.
    /// </summary>
    private bool IsOnContextMenu
    {
        get
        {
            ToolStrip mainStrip = GetMainToolStrip();
            if (mainStrip is not null && mainStrip.Site is not null && !(mainStrip is ContextMenuStrip))
            {
                return false;
            }

            return true;
        }
    }

    /// <summary>
    ///  Easy method for getting to the ToolStripDropDownItem
    /// </summary>
    private ToolStripDropDownItem MenuItem
    {
        get => ToolStripItem as ToolStripDropDownItem;
    }

    /// <summary>
    ///  This property is true when the OwnerItem is selected during COPY and PASTE operations.
    /// </summary>
    private bool MenuItemSelected
    {
        get
        {
            // check to see if the primary selection is the ToolStrip or one of it's children.
            if (_selectionService is not null)
            {
                object selectedItem = _selectionService.PrimarySelection;
                ToolStripItem toolItem;
                if (selectedItem is null)
                {
                    if (KeyboardHandlingService is not null)
                    {
                        selectedItem = KeyboardHandlingService.SelectedDesignerControl;
                    }

                    toolItem = selectedItem as ToolStripItem;
                }
                else
                {
                    toolItem = selectedItem as ToolStripItem;
                }

                if (toolItem is not null)
                {
                    // We always need to return the current selection if we are adding a DummyNode...
                    if (_designerHost is not null)
                    {
                        if (_designerHost.GetDesigner(toolItem) is ToolStripItemDesigner designer)
                        {
                            if (designer._dummyItemAdded)
                            {
                                return (toolItem == MenuItem);
                            }
                        }
                    }

                    // We need to return parent if we are on DropDown...
                    if (toolItem.IsOnDropDown && toolItem.Owner is ToolStripDropDown dropDown)
                    {
                        ToolStripDropDownItem parentItem = dropDown.OwnerItem as ToolStripDropDownItem;
                        return (parentItem == MenuItem);
                    }
                    else
                    {
                        return (toolItem == MenuItem);
                    }
                }
                else if (selectedItem is ContextMenuStrip menuStrip
                    // VSO 214130--SelectedItem must belong to this designer
                    && menuStrip.OwnerItem == Component
                    && MenuItem.DropDown == selectedItem)
                {
                    return true;
                }
            }

            return false;
        }
    }

    private ToolStripKeyboardHandlingService KeyboardHandlingService
        => _keyboardHandlingService ??= GetService<ToolStripKeyboardHandlingService>();

    /// <summary>
    ///  ParentComponent in case of MenuItems on the DropDown is the OwnerItem of the DropDown and
    ///  not the ToolStripDropDown (since the DropDowns are not sited).
    /// </summary>
    protected override IComponent ParentComponent
    {
        get
        {
            if (ToolStripItem is not null)
            {
                if (!ToolStripItem.IsOnOverflow && ToolStripItem.IsOnDropDown)
                {
                    // If the dropDown is NOT AutoGenerated then its a dropdown the user has set or its a
                    // contextMenuStrip/ToolStripDropDownMenu/ToolStripDropDown that has been added from the ToolBox.
                    // In such a case don't return the ownerItem but the DropDown itself as the ParentComponent.
                    if (MenuItem.Owner is ToolStripDropDown dropDown)
                    {
                        if (dropDown.IsAutoGenerated)
                        {
                            return dropDown.OwnerItem;
                        }
                        else
                        {
                            return dropDown;
                        }
                    }
                }

                return base.ParentComponent;
            }

            return null;
        }
    }

    private IComponentChangeService ComponentChangeService => _componentChangeService ??= GetService<IComponentChangeService>();

    /// <summary>
    ///  Adds the dummy node for InSitu Edit.
    /// </summary>
    internal void AddNewTemplateNode(ToolStripDropDown dropDown)
    {
        // Check if the DropDown contains a typeHereNode....
        foreach (ToolStripItem currentItem in dropDown.Items)
        {
            if (currentItem is DesignerToolStripControlHost host)
            {
                _typeHereNode = host;
            }
        }

        if (_typeHereNode is not null)
        {
            dropDown.Items.Remove(_typeHereNode);
        }

        // setup the MINIToolStrip host...
        _typeHereTemplateNode = new ToolStripTemplateNode(Component, SR.ToolStripDesignerTemplateNodeEnterText);
        int width = _typeHereTemplateNode.EditorToolStrip.Width;
        _typeHereNode = new DesignerToolStripControlHost(_typeHereTemplateNode.EditorToolStrip);
        _typeHereTemplateNode.ControlHost = _typeHereNode;
        _typeHereNode.AutoSize = false;
        _typeHereNode.Width = width;
        dropDown.Items.Add(_typeHereNode);
    }

    // used by morphing in ToolStripItemDesigner ... hence internal method.
    internal void AddItemBodyGlyph(ToolStripItem item)
    {
        if (item is not null)
        {
            ToolStripItemDesigner dropDownItemDesigner = (ToolStripItemDesigner)_designerHost.GetDesigner(item);
            if (dropDownItemDesigner is not null)
            {
                Rectangle bounds = dropDownItemDesigner.GetGlyphBounds();
                Behavior.Behavior toolStripBehavior = new ToolStripItemBehavior();
                // Initialize Glyph
                ToolStripItemGlyph bodyGlyphForddItem = new(item, dropDownItemDesigner, bounds, toolStripBehavior);
                // Set the glyph for the item .. so that we can remove it later....
                dropDownItemDesigner._bodyGlyph = bodyGlyphForddItem;
                // Add ItemGlyph to the Collection
                _toolStripAdornerWindowService?.DropDownAdorner.Glyphs.Insert(0, bodyGlyphForddItem);
            }
        }
    }

    // Add individual item Body Glyphs
    private void AddBodyGlyphs(ToolStripDropDownItem item)
    {
        if (item is not null)
        {
            ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)_designerHost.GetDesigner(item);
            if (itemDesigner is not null)
            {
                foreach (ToolStripItem ddItem in item.DropDownItems)
                {
                    AddItemBodyGlyph(ddItem);
                }
            }
        }
    }

    /// <summary>
    ///  This is called by the TemplateNode to Commit the Edit. This function creates a NEW ToolStripDropDownItem
    ///  if we are committing a dummy node or else just replaces the TEXT and IMAGE if we are changing
    ///  an existing MenuItem.
    /// </summary>
    internal override void CommitEdit(Type type, string text, bool commit, bool enterKeyPressed, bool tabKeyPressed)
    {
        IsEditorActive = false;
        if (!(MenuItem.Owner is ToolStripDropDown) && base.Editor is not null) // we are on Base MenuStrip !!
        {
            base.CommitEdit(type, text, commit, enterKeyPressed, tabKeyPressed);
        }
        else if (commit)
        {
            bool dummyItem = _dummyItemAdded;
            _dummyItemAdded = false;
            MenuItem.DropDown.SuspendLayout();
            int index;
            if (_commitedEditorNode is not null)
            {
                // This means we have a valid node and we just changed some properties.
                index = MenuItem.DropDownItems.IndexOf(_commitedEditorNode);
                ToolStripItem editedItem = MenuItem.DropDownItems[index + 1];

                // Remove TemplateNode
                MenuItem.DropDown.Items.Remove(_commitedEditorNode);

                // Get rid of the templateNode.
                _commitedTemplateNode?.CloseEditor();
                _commitedTemplateNode = null;

                _commitedEditorNode.Dispose();
                _commitedEditorNode = null;

                // If we have type "-" this means the user wants to add a Separator.
                if (text == "-")
                {
                    if (_designerHost.GetDesigner(editedItem) is ToolStripItemDesigner itemDesigner)
                    {
                        try
                        {
                            editedItem = itemDesigner.MorphCurrentItem(typeof(ToolStripSeparator));
                            // Remove the ActionGlyph Added by morphing
                            RemoveItemBodyGlyph(editedItem);
                        }
                        catch
                        {
                            if (_newMenuItemTransaction is not null)
                            {
                                try
                                {
                                    _newMenuItemTransaction.Cancel();
                                }
                                catch // This will cause ROLLBACK and hence throw if the project is already in DEBUG and instuEdit was Active.
                                {
                                }

                                _newMenuItemTransaction = null;
                            }
                        }
                        finally
                        {
                            _newMenuItemTransaction?.Commit();
                            _newMenuItemTransaction = null;
                        }
                    }
                }
                else
                {
                    // We are adding the item through InSitu for the first time
                    if (dummyItem)
                    {
                        try
                        {
                            _dummyItemAdded = true;
                            CreateNewItem(type, index, text);
                            _designerHost.DestroyComponent(editedItem);
                            // One place where we need to call this explicitly since the selection doesn't change.
                            if (enterKeyPressed)
                            {
                                _typeHereNode.SelectControl();
                            }
                        }
                        catch
                        {
                            if (_newMenuItemTransaction is not null)
                            {
                                try
                                {
                                    _newMenuItemTransaction.Cancel();
                                }
                                catch // This will cause ROLLBACK and hence throw if the project is already in DEBUG and instuEdit was Active.
                                {
                                }

                                _newMenuItemTransaction = null;
                            }
                        }
                        finally
                        {
                            _newMenuItemTransaction?.Commit();
                            _newMenuItemTransaction = null;

                            _dummyItemAdded = false;
                        }
                    }
                    else // We are editing item that was already present
                    {
                        // put the item back...
                        editedItem.Visible = true;

                        // create our transaction
                        DesignerTransaction designerTransaction = _designerHost.CreateTransaction(SR.ToolStripItemPropertyChangeTransaction);
                        try
                        {
                            // Change the properties
                            PropertyDescriptor textProp = TypeDescriptor.GetProperties(editedItem)["Text"];
                            string oldValue = (string)textProp.GetValue(editedItem);
                            if (textProp is not null && text != oldValue)
                            {
                                textProp.SetValue(editedItem, text);
                            }
                        }
                        catch
                        {
                            designerTransaction?.Cancel();
                            designerTransaction = null;
                        }
                        finally
                        {
                            designerTransaction?.Commit();
                        }
                    }
                }
            }
            else
            {
                // if committedEditorNode is null and we are in commitEdit then just add the new Item, since this item is added through dropDown.
                // if no editorNode then we are committing a NEW NODE...
                index = MenuItem.DropDownItems.IndexOf(_typeHereNode);
                try
                {
                    _dummyItemAdded = true;
                    CreateNewItem(type, index, text);
                }
                finally
                {
                    _dummyItemAdded = false;
                }

                // One place where we need to call this explicitly since the selection doesn't change.
                _typeHereNode.SelectControl();
            }

            // Invalidate DropDown..
            MenuItem.DropDown.ResumeLayout(true);
            MenuItem.DropDown.PerformLayout();
            // Reset the Glyphs after Item addition...
            ResetGlyphs(MenuItem);
            // set the selection to our new item only if item was committed via ENTER KEY in the InSitu Mode.
            if (_selectionService is not null)
            {
                if (enterKeyPressed)
                {
                    ToolStripItem nextItem = (MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveLeft ||
                        MenuItem.DropDownDirection == ToolStripDropDownDirection.AboveRight) && index >= 1
                        ? MenuItem.DropDownItems[index - 1]
                        : MenuItem.DropDownItems[index + 1];

                    if (KeyboardHandlingService is not null)
                    {
                        if (nextItem is not null)
                        {
                            if (MenuItem.DropDownItems[index] is ToolStripDropDownItem currentItem)
                            {
                                currentItem.HideDropDown();
                            }
                        }

                        if (nextItem == _typeHereNode)
                        {
                            // Put Selection on the TypeHereNode.
                            KeyboardHandlingService.SelectedDesignerControl = nextItem;
                            _selectionService.SetSelectedComponents(null, SelectionTypes.Replace);
                        }
                        else
                        {
                            KeyboardHandlingService.SelectedDesignerControl = null;
                            _selectionService.SetSelectedComponents(new object[] { nextItem });
                        }
                    }
                }
                else if (tabKeyPressed) // just select this the new Item
                {
                    _selectionService.SetSelectedComponents(new object[] { MenuItem.DropDownItems[index] }, SelectionTypes.Replace);
                }
            }
        }
        else
        {
            // We come here if we have not committed so revert our state.
            if (_commitedEditorNode is not null)
            {
                // We just changed some properties which we want to revert.
                MenuItem.DropDown.SuspendLayout();
                bool dummyItem = _dummyItemAdded;
                _dummyItemAdded = false;
                int index = MenuItem.DropDownItems.IndexOf(_commitedEditorNode);
                ToolStripItem editedItem = MenuItem.DropDownItems[index + 1];
                MenuItem.DropDown.Items.Remove(_commitedEditorNode);
                // put the item back...
                editedItem.Visible = true;
                _commitedTemplateNode?.CloseEditor();
                _commitedTemplateNode = null;

                _commitedEditorNode?.Dispose();
                _commitedEditorNode = null;

                if (dummyItem)
                {
                    MenuItem.DropDownItems.Remove(editedItem);
                    try
                    {
                        _designerHost.DestroyComponent(editedItem);
                    }
                    catch
                    {
                        if (_newMenuItemTransaction is not null)
                        {
                            try
                            {
                                _newMenuItemTransaction.Cancel();
                            }
                            catch // This will cause ROLLBACK and hence throw if the project is already in DEBUG and instuEdit was Active.
                            {
                            }

                            _newMenuItemTransaction = null;
                        }
                    }

                    editedItem = null;
                }

                // Required to do here...
                MenuItem.DropDown.ResumeLayout();

                // Remove the Glyphs so that Mouse Message go to the Editor
                if (editedItem is not null)
                {
                    AddItemBodyGlyph(editedItem);
                }

                if (dummyItem)
                {
                    // Since the operation is cancelled and there is no change of glyphs,
                    // set SelectionManager.NeedRefresh to false so that it doesn't refresh,
                    // when the transaction is cancelled.
                    GetService<SelectionManager>().NeedRefresh = false;
                    if (_newMenuItemTransaction is not null)
                    {
                        try
                        {
                            _dummyItemAdded = true;
                            _newMenuItemTransaction.Cancel();
                            _newMenuItemTransaction = null;
                            if (MenuItem.DropDownItems.Count == 0)
                            {
                                CreatetypeHereNode();
                            }
                        }
                        finally
                        {
                            _dummyItemAdded = false;
                        }
                    }
                }

                // Added for Separators...
                MenuItem.DropDown.PerformLayout();
            }
        }
    }

    /// <summary>
    ///  Creates the dummy node for InSitu Edit.
    /// </summary>
    private void CreatetypeHereNode()
    {
        if (_typeHereNode is null)
        {
            AddNewTemplateNode(MenuItem.DropDown);
            // Add Text for Debugging Non Sited DropDown..
            if (MenuItem.DropDown.Site is null)
            {
                MenuItem.DropDown.Text = $"{MenuItem.Name}.DropDown";
            }
        }
        else if (_typeHereNode is not null && MenuItem.DropDownItems.IndexOf(_typeHereNode) == -1)
        {
            MenuItem.DropDown.Items.Add(_typeHereNode);
            _typeHereNode.Visible = true;
        }

        // Invalidate DropDown..
        MenuItem.DropDown.PerformLayout();
    }

    /// <summary>
    ///  This Function is called by EnterInSituEdit where in we Swap the typeHereNode by
    ///  the TemplateNode (the InSitu Editor). Since the TemplateNode had a EditorToolStrip we can just HOST
    ///  that ToolStrip as a ToolStripControlHost and add it to the DropDown.
    /// </summary>
    private void CreateDummyMenuItem(ToolStripItem item, string text)
    {
        _commitedTemplateNode = new ToolStripTemplateNode(Component, text)
        {
            ActiveItem = item
        };
        int width = _commitedTemplateNode.EditorToolStrip.Width;
        _commitedEditorNode = new DesignerToolStripControlHost(_commitedTemplateNode.EditorToolStrip)
        {
            AutoSize = false,
            Width = width
        };
    }

    /// <summary>
    ///  This helper function creates a dummyItem for InSitu editing.
    /// </summary>
    private ToolStripItem CreateDummyItem(Type t, int dummyIndex)
    {
        if (_designerHost is null)
        {
            Debug.Fail("Couldn't get designer host!");
            return null;
        }

        ToolStripItem newItem = null;
        // For the "Upward DropDown" add at index +1...
        if (MenuItem.DropDownDirection is ToolStripDropDownDirection.AboveLeft or ToolStripDropDownDirection.AboveRight)
        {
            dummyIndex++;
        }

        try
        {
            // turn off Adding/Added events listened to by the ToolStripDesigner...
            ToolStripDesigner.s_autoAddNewItems = false;

            // Store the index into a class level member so that the componentChanged can access it
            _indexToInsertNewItem = dummyIndex;

            try
            {
                // create our transaction
                _newMenuItemTransaction ??= _designerHost.CreateTransaction(SR.ToolStripCreatingNewItemTransaction);

                _fireComponentChanged = true;
                newItem = (ToolStripItem)_designerHost.CreateComponent(t);
            }
            finally
            {
                _fireComponentChanged = false;
            }

            ToolStripItemDesigner designer = _designerHost.GetDesigner(newItem) as ToolStripItemDesigner;
            try
            {
                // ToolStripItem designer tries to set the TEXT for the item in the InitializeNewComponent().
                // But since we are create item thru InSitu .. we shouldn't do this.
                designer.InternalCreate = true;
                designer.InitializeNewComponent(null);
            }
            finally
            {
                designer.InternalCreate = false;
            }
        }
        catch (InvalidOperationException ex)
        {
            CommitInsertTransaction(commit: false);

            _newMenuItemTransaction?.Cancel();
            _newMenuItemTransaction = null;

            GetService<IUIService>().ShowError(ex.Message);
        }
        finally
        {
            // turn on Adding/Added events listened to by the ToolStripDesigner...
            ToolStripDesigner.s_autoAddNewItems = true;
            // Reset the index
            _indexToInsertNewItem = -1;
        }

        return newItem;
    }

    /// <summary>
    ///  Asks the host to create a new DropDownItem, inserts the item into the collection and selects it.
    /// </summary>
    private ToolStripItem CreateNewItem(Type t, int dummyIndex, string newText)
    {
        if (_designerHost is null)
        {
            Debug.Fail("Couldn't get designer host!");
            return null;
        }

        ToolStripItem newItem = null;
        // For the "Upward DropDown" add at index +1...
        if (MenuItem.DropDownDirection is ToolStripDropDownDirection.AboveLeft or ToolStripDropDownDirection.AboveRight)
        {
            dummyIndex++;
        }

        // create our transaction
        DesignerTransaction outerTransaction = _designerHost.CreateTransaction(SR.ToolStripCreatingNewItemTransaction);
        try
        {
            // turn off Adding/Added events listened to by the ToolStripDesigner...
            ToolStripDesigner.s_autoAddNewItems = false;
            // Store the index into a class level member so that the componentChanged can access it
            _indexToInsertNewItem = dummyIndex;
            try
            {
                _fireComponentChanged = true;
                newItem = (ToolStripItem)_designerHost.CreateComponent(t, ToolStripDesigner.NameFromText(newText, t, MenuItem.Site));
            }
            finally
            {
                _fireComponentChanged = false;
            }

            ToolStripItemDesigner designer = _designerHost.GetDesigner(newItem) as ToolStripItemDesigner;
            try
            {
                // ToolStripItem designer tries to set the TEXT for the item in the InitializeNewComponent().
                // But since we are create item thru InSitu .. we shouldn't do this.
                if (!string.IsNullOrEmpty(newText) || _addingDummyItem)
                {
                    designer.InternalCreate = true;
                }

                designer?.InitializeNewComponent(null);
            }
            finally
            {
                designer.InternalCreate = false;
            }

            // Set the Text and Image..
            if (newItem is not null)
            {
                PropertyDescriptor textProperty = TypeDescriptor.GetProperties(newItem)["Text"];
                Debug.Assert(textProperty is not null, "Could not find 'Text' property in ToolStripItem.");
                if (textProperty is not null && !string.IsNullOrEmpty(newText))
                {
                    textProperty.SetValue(newItem, newText);
                }
            }
        }
        catch
        {
            // There might be scenarios where the ComponentAdding is fired but the Added doesn't get fired.
            // Is such cases the InsertTransaction might be still active... So we need to cancel that too here.
            CommitInsertTransaction(false);
            outerTransaction?.Cancel();
            outerTransaction = null;
        }
        finally
        {
            outerTransaction?.Commit();

            // turn on Adding/Added events listened to by the ToolStripDesigner...
            ToolStripDesigner.s_autoAddNewItems = true;
            // Reset the index
            _indexToInsertNewItem = -1;
        }

        return newItem;
    }

    /// <summary>
    ///  Helper function to find whether the passed in DropDownItems have same owner.
    /// </summary>
    private static bool CheckSameOwner(ToolStripDropDownItem lastSelected, ToolStripDropDownItem currentSelected)
    {
        if (lastSelected is not null && currentSelected is not null)
        {
            if (lastSelected.Owner is ToolStripDropDown lastDropDown && currentSelected.Owner is ToolStripDropDown currentDropDown)
            {
                ToolStripItem ownerLastSelected = lastDropDown.OwnerItem;
                ToolStripItem ownerCurrentSelected = currentDropDown.OwnerItem;
                return (ownerLastSelected == ownerCurrentSelected);
            }
        }

        return false;
    }

    // internal method to commit any node.
    internal void Commit()
    {
        if (_commitedTemplateNode is not null && _commitedTemplateNode.Active)
        {
            // Get Index of the CommittedItem..
            int index = MenuItem.DropDownItems.IndexOf(_commitedEditorNode);
            _commitedTemplateNode.Commit(false, false);
            if (index != -1 && MenuItem.DropDownItems.Count > index)
            {
                if (MenuItem.DropDownItems[index] is ToolStripDropDownItem newItem)
                {
                    newItem.HideDropDown();
                }
            }
        }
        else if (_typeHereTemplateNode is not null && _typeHereTemplateNode.Active)
        {
            _typeHereTemplateNode.Commit(false, false);
        }

        // COMMIT ALL THE THE PARENT CHAIN ....
        ToolStripDropDownItem currentItem = MenuItem;
        while (currentItem?.Owner is ToolStripDropDown dropDown)
        {
            currentItem = (ToolStripDropDownItem)dropDown.OwnerItem;
            if (currentItem is not null)
            {
                ToolStripMenuItemDesigner itemDesigner = (ToolStripMenuItemDesigner)_designerHost.GetDesigner(currentItem);
                itemDesigner?.Commit();
            }
        }
    }

    /// <summary>
    ///  Disposes of this designer.
    /// </summary>
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // clean up
            if (_selectionService is not null)
            {
                _selectionService.SelectionChanged -= OnSelectionChanged;
            }

            if (_undoEngine is not null)
            {
                _undoEngine.Undoing -= OnUndoing;
                _undoEngine.Undone -= OnUndone;
            }

            if (MenuItem is not null && MenuItem.HasDropDown)
            {
                MenuItem.DropDown.Hide();
                UnHookEvents();
            }

            if (ComponentChangeService is not null)
            {
                ComponentChangeService.ComponentAdding -= ComponentChangeSvc_ComponentAdding;
                ComponentChangeService.ComponentAdded -= ComponentChangeSvc_ComponentAdded;

                ComponentChangeService.ComponentRemoving -= ComponentChangeSvc_ComponentRemoving;
                ComponentChangeService.ComponentRemoved -= ComponentChangeSvc_ComponentRemoved;
            }

            if (_toolStripAdornerWindowService is not null)
            {
                _toolStripAdornerWindowService = null;
            }

            if (_typeHereTemplateNode is not null)
            {
                _typeHereTemplateNode.RollBack();
                _typeHereTemplateNode.CloseEditor();
                _typeHereTemplateNode = null;
            }

            _typeHereNode?.Dispose();
            _typeHereNode = null;

            if (_commitedTemplateNode is not null)
            {
                _commitedTemplateNode.RollBack();
                _commitedTemplateNode.CloseEditor();
                _commitedTemplateNode = null;
            }

            _commitedEditorNode?.Dispose();
            _commitedEditorNode = null;

            if (_parentItem is not null)
            {
                _parentItem = null;
            }
        }

        base.Dispose(disposing);
    }

    // When the dropDown is clicked; Commit any active InSitu node.
    private void DropDownClick(object sender, EventArgs e)
    {
        // Commit any InsituEdit Node.
        if (KeyboardHandlingService is not null && KeyboardHandlingService.TemplateNodeActive)
        {
            // If templateNode Active .. commit and Select it
            KeyboardHandlingService.ActiveTemplateNode.CommitAndSelect();
        }
    }

    // re-paint required to "validate" the glyphs
    private void DropDownPaint(object sender, PaintEventArgs e)
    {
        // Select All requires the repaint of glyphs after the DropDown receives paint message.
        if (_selectionService is not null && MenuItem is not null)
        {
            foreach (ToolStripItem item in MenuItem.DropDownItems)
            {
                if (item.Visible && _selectionService.GetComponentSelected(item))
                {
                    if (_designerHost.GetDesigner(item) is ToolStripItemDesigner designer)
                    {
                        Rectangle r = designer.GetGlyphBounds();
                        ToolStripDesignerUtils.GetAdjustedBounds(item, ref r);
                        r.Inflate(GLYPHINSET, GLYPHINSET);

                        // This will allow any Glyphs to re-paint after this control and its designer has painted
                        GetService<BehaviorService>()?.ProcessPaintMessage(r);
                    }
                }

                // When you alt-tab from VS to any other application and come back and if the dropDown is open;
                // then the templateNode doesn't paint. This happens when you have another control below the dropDown
                // which paints over the controlhost so we need to refresh the TemplateNode in this case.
                if (item is DesignerToolStripControlHost controlHost)
                {
                    controlHost.Control.Refresh();
                }
            }
        }
    }

    // Invalidate the BehaviorService if the location changed.
    private void DropDownLocationChanged(object sender, EventArgs e)
    {
        // this shouldn't get fired many times.. only in certain case...
        // but in those cases its needed to REFRESH THE ENTIRE adornerWindow.
        ToolStripDropDown dropDown = sender as ToolStripDropDown;
        if (dropDown.Visible)
        {
            GetService<BehaviorService>()?.Invalidate();
        }
    }

    // Change the parent when the DropDown is opening.
    private void DropDownItem_DropDownOpening(object sender, EventArgs e)
    {
        ToolStripDropDownItem ddi = sender as ToolStripDropDownItem;
        if (_toolStripAdornerWindowService is not null)
        {
            ddi.DropDown.TopLevel = false;
            ddi.DropDown.Parent = _toolStripAdornerWindowService.ToolStripAdornerWindowControl;
        }
    }

    // Add the DropDownGlyph when the dropDown is opened.
    private void DropDownItem_DropDownOpened(object sender, EventArgs e)
    {
        // Add Glyphs...
        ToolStripDropDownItem ddi = sender as ToolStripDropDownItem;
        if (ddi is not null)
        {
            ResetGlyphs(ddi);
        }

        // finally add Glyph for the "DropDown"
        Control rootControl = ddi.DropDown;
        if (rootControl is not null)
        {
            if (_designerHost.GetDesigner(_designerHost.RootComponent) is ControlDesigner designer)
            {
                _rootControlGlyph = new ToolStripDropDownGlyph(rootControl.Bounds, new DropDownBehavior(designer, this));
            }

            _toolStripAdornerWindowService?.DropDownAdorner.Glyphs.Add(_rootControlGlyph);
        }
    }

    // Remove the dropDownGlyph after the dropDown is closed.
    private void DropDownItem_DropDownClosed(object sender, EventArgs e)
    {
        if (sender is ToolStripDropDownItem ddi)
        {
            // Invalidate the ToolStripWindow.... for clearing the dropDowns
            if (_toolStripAdornerWindowService is not null)
            {
                if (_rootControlGlyph is not null && _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(_rootControlGlyph))
                {
                    _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(_rootControlGlyph);
                }
            }

            // Remove body Glyphs...
            InitializeBodyGlyphsForItems(false /*remove*/, ddi);
            // Unhook all the Events
            _initialized = false;
            UnHookEvents();
            // Check if this is a Sited-DropDown
            if (ddi.DropDown.Site is not null || ddi.DropDownItems.Count == 1)
            {
                // Get Designer ... and call Remove on that Designer.
                RemoveTypeHereNode(ddi);
            }
            else
            {
                _toolStripAdornerWindowService?.Invalidate(ddi.DropDown.Bounds);
            }
        }
    }

    // invalidate the AdornerWindow when the DropDown resizes
    private void DropDownResize(object sender, EventArgs e)
    {
        ToolStripDropDown dropDown = sender as ToolStripDropDown;
        if (!_dummyItemAdded)
        {
            if (dropDown is not null && dropDown.Visible)
            {
                // Invalidate only if new Size is LESS than old Size...
                if (_toolStripAdornerWindowService is not null && (dropDown.Width < _dropDownSizeToInvalidate.Width || dropDown.Size.Height < _dropDownSizeToInvalidate.Height))
                {
                    using Region invalidatingRegion = new(_dropDownSizeToInvalidate);
                    invalidatingRegion.Exclude(dropDown.Bounds);
                    _toolStripAdornerWindowService.Invalidate(invalidatingRegion);

                    // Invalidate BehaviorService AdornerWindow as well... but only the DropDownBounds
                    GetService<BehaviorService>()?.Invalidate(invalidatingRegion);
                }
            }

            if (_toolStripAdornerWindowService is not null)
            {
                if (_rootControlGlyph is not null && _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(_rootControlGlyph))
                {
                    _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(_rootControlGlyph);
                }

                if (_designerHost.GetDesigner(_designerHost.RootComponent) is ControlDesigner designer)
                {
                    _rootControlGlyph = new ToolStripDropDownGlyph(dropDown.Bounds, new DropDownBehavior(designer, this));
                }

                _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Add(_rootControlGlyph);
            }
        }

        _dropDownSizeToInvalidate = dropDown.Bounds;
    }

    /// <summary>
    ///  Called when a menuItem wants to go into InSitu Editing Mode.
    /// </summary>
    internal void EditTemplateNode(bool clicked)
    {
        // If the parent has a window which is too small, there won't be any space to draw the entry box and typeHereNode will be null
        if (_typeHereNode is null)
        {
            return;
        }

        // Refresh the state of the 'TypeHere' node to NotSelected state
        _typeHereNode.RefreshSelectionGlyph();
        // Commit any InsituEdit Node.
        if (KeyboardHandlingService is not null && KeyboardHandlingService.TemplateNodeActive)
        {
            // If templateNode Active .. commit and Select it
            KeyboardHandlingService.ActiveTemplateNode.CommitAndSelect();
        }

        // commit any existing InSitu editor...
        if (clicked)
        {
            // We should come here for a valid parent !!!
            if (MenuItem is null)
            {
                return;
            }
        }

        // always select the parent item... but don't redraw the control during this Selection Change as this causes flicker
        try
        {
            ToolStripDesigner.s_editTemplateNode = true;
            _selectionService.SetSelectedComponents(new object[] { MenuItem }, SelectionTypes.Replace);
        }
        finally
        {
            ToolStripDesigner.s_editTemplateNode = false;
        }

        // Hide the DropDown of any previous Selection.. First get the SelectedItem...
        ToolStripDropDownItem selectedItem = null;
        if (_selectionService.PrimarySelection is null && KeyboardHandlingService is not null)
        {
            if (KeyboardHandlingService.SelectedDesignerControl is ToolStripItem item)
            {
                selectedItem = ((ToolStripDropDown)item.Owner).OwnerItem as ToolStripDropDownItem;
            }
        }
        else
        {
            selectedItem = _selectionService.PrimarySelection as ToolStripDropDownItem;
        }

        // Now Hide the DropDown and Refresh Glyphs...
        if (selectedItem is not null && selectedItem != MenuItem)
        {
            HideSiblingDropDowns(selectedItem);
        }

        MenuItem.DropDown.SuspendLayout();
        _dummyItemAdded = true;
        int index = MenuItem.DropDownItems.IndexOf(_typeHereNode);
        ToolStripItem newDummyItem = null;
        try
        {
            _addingDummyItem = true;
            newDummyItem = CreateDummyItem(typeof(ToolStripMenuItem), index);
        }
        catch (CheckoutException checkoutException)
        {
            if (checkoutException.Equals(CheckoutException.Canceled))
            {
                CommitInsertTransaction(commit: false);

                _newMenuItemTransaction?.Cancel();
                _newMenuItemTransaction = null;
            }
            else
            {
                throw;
            }
        }
        finally
        {
            _dummyItemAdded = (newDummyItem is not null);
            _addingDummyItem = false;
        }

        MenuItem.DropDown.ResumeLayout();
        if (newDummyItem is not null)
        {
            if (_designerHost.GetDesigner(newDummyItem) is ToolStripMenuItemDesigner newItemDesigner)
            {
                newItemDesigner.InitializeDropDown();
                newItemDesigner.ShowEditNode(clicked);
            }
        }
    }

    /// <summary>
    ///  Called from OnDoubleClickTimerTick to Enter in InSituMode
    /// </summary>
    private void EnterInSituMode()
    {
        // we need to tell our parent that we want to enter InSitu edit mode
        if (MenuItem.Owner is ToolStripDropDown dropDown)
        {
            ToolStripItem ownerItem = dropDown.OwnerItem;
            // need to inform the owner tha we want to enter InSitu mode
            if (_designerHost is not null)
            {
                IDesigner designer = _designerHost.GetDesigner(ownerItem);
                if (designer is ToolStripMenuItemDesigner menuItemDesigner)
                {
                    // Need to Add Dummy Node For Direct InSitu..
                    MenuItem.HideDropDown();
                    menuItemDesigner.EnterInSituEdit(MenuItem);
                }
            }
        }
    }

    /// <summary>
    ///  This method replaces the menItem with an in-situ TemplateNode.
    /// </summary>
    internal void EnterInSituEdit(ToolStripItem toolItem)
    {
        MenuItem.DropDown.SuspendLayout();
        // Remove the Glyphs so that Mouse Message go to the Editor
        RemoveItemBodyGlyph(toolItem);

        if (toolItem is null)
        {
            return;
        }

        // we can only one Active Editor at one time.
        if (IsEditorActive)
        {
            return;
        }

        CreateDummyMenuItem(toolItem, toolItem.Text);
        int index = MenuItem.DropDownItems.IndexOf(toolItem);
        // swap in our InSitu ToolStrip
        MenuItem.DropDownItems.Insert(index, _commitedEditorNode);
        if (toolItem is ToolStripControlHost host)
        {
            host.Control.Visible = false;
        }

        toolItem.Visible = false;
        MenuItem.DropDown.ResumeLayout();
        // Try Focusing the TextBox....
        _commitedTemplateNode?.FocusEditor(toolItem);

        ToolStripDropDownItem dropDownItem = toolItem as ToolStripDropDownItem;
        if (!(dropDownItem.Owner is ToolStripDropDownMenu) && dropDownItem is not null && dropDownItem.Bounds.Width < _commitedEditorNode.Bounds.Width)
        {
            dropDownItem.Width = _commitedEditorNode.Width;
            dropDownItem.DropDown.Location = new Point(dropDownItem.DropDown.Location.X + _commitedEditorNode.Bounds.Width - dropDownItem.Bounds.Width, dropDownItem.DropDown.Location.Y);
        }

        IsEditorActive = true;
    }

    /// <summary>
    ///  Get the Insertion Index to drop the current drag-drop item.
    /// </summary>
    private static int GetItemInsertionIndex(ToolStripDropDown wb, Point ownerClientAreaRelativeDropPoint)
    {
        for (int i = 0; i < wb.Items.Count; i++)
        {
            Rectangle bounds = wb.Items[i].Bounds;
            bounds.Inflate(wb.Items[i].Margin.Size);
            if (bounds.Contains(ownerClientAreaRelativeDropPoint))
            {
                return wb.Items.IndexOf(wb.Items[i]);
            }
        }

        return -1;
    }

    /// <summary>
    ///  Returns the DropDown for this MenuItem else returns the Parent(Owner).
    /// </summary>
    protected override Component GetOwnerForActionList()
    {
        return MenuItem;
    }

    // Helper Function to get the Main ToolStrip (MenuStrip);
    internal override ToolStrip GetMainToolStrip()
    {
        ToolStripDropDown topmost = GetFirstDropDown(MenuItem);
        ToolStripItem topMostItem = topmost?.OwnerItem;
        if (topMostItem is not null)
        {
            return topMostItem.Owner;
        }

        return MenuItem.Owner;
    }

    /// <summary>
    ///  Helper function to Hide the Active Dropdown from the given DropDownItem.
    /// </summary>
    private void HideAllDropDowns(ToolStripDropDownItem item)
    {
        try
        {
            if (MenuItem.Owner is ToolStripDropDown dropDown)
            {
                ToolStripItem ownerItem = dropDown.OwnerItem;
                while (item != ownerItem)
                {
                    if (item.DropDown.Visible)
                    {
                        item.HideDropDown();
                    }

                    if (item.Owner is ToolStripDropDown itemDropDown)
                    {
                        item = (ToolStripDropDownItem)itemDropDown.OwnerItem;
                    }
                    else
                    {
                        return;
                    }
                }
            }
        }
        catch (Exception e) when (!e.IsCriticalException())
        {
        }
    }

    /// <summary>
    ///  Helper function to Hide the Active Dropdown for all the siblings of the given DropDownItem.
    /// </summary>
    private void HideSiblingDropDowns(ToolStripDropDownItem item)
    {
        try
        {
            ToolStripItem ownerItem = MenuItem;
            while (item != ownerItem)
            {
                item.HideDropDown();
                if (item.Owner is ToolStripDropDown dropDown)
                {
                    item = (ToolStripDropDownItem)dropDown.OwnerItem;
                }
                else
                {
                    return;
                }
            }
        }
        catch (Exception e) when (!e.IsCriticalException())
        {
        }
    }

    /// <summary>
    ///  This will listen to the necessary dropDown events.
    ///  Now we add the events on selection and unhook when the dropDown is closed.
    /// </summary>
    internal void HookEvents()
    {
        if (MenuItem is not null)
        {
            MenuItem.DropDown.Closing += OnDropDownClosing;
            MenuItem.DropDownOpening += DropDownItem_DropDownOpening;
            MenuItem.DropDownOpened += DropDownItem_DropDownOpened;
            MenuItem.DropDownClosed += DropDownItem_DropDownClosed;
            MenuItem.DropDown.Resize += DropDownResize;
            MenuItem.DropDown.ItemAdded += OnItemAdded;
            MenuItem.DropDown.Paint += DropDownPaint;
            MenuItem.DropDown.Click += DropDownClick;
            MenuItem.DropDown.LocationChanged += DropDownLocationChanged;
        }
    }

    /// <summary>
    ///  Initializes the ToolStripDropDownItem Designer.
    /// </summary>
    public override void Initialize(IComponent component)
    {
        base.Initialize(component);

        // Initialize the properties we will be shadowing
        Visible = true;
        DoubleClickEnabled = MenuItem.DoubleClickEnabled;

        // Hook our services
        if (TryGetService(out _selectionService))
        {
            _selectionService.SelectionChanged += OnSelectionChanged;
        }

        // Hookup to the AdornerService
        _toolStripAdornerWindowService = GetService<ToolStripAdornerWindowService>();
        _designerHost = GetService<IDesignerHost>();

        // Set the DoubleClickEnabled
        MenuItem.DoubleClickEnabled = true;

        if (ComponentChangeService is not null)
        {
            ComponentChangeService.ComponentAdding += ComponentChangeSvc_ComponentAdding;
            ComponentChangeService.ComponentAdded += ComponentChangeSvc_ComponentAdded;

            ComponentChangeService.ComponentRemoving += ComponentChangeSvc_ComponentRemoving;
            ComponentChangeService.ComponentRemoved += ComponentChangeSvc_ComponentRemoved;
        }

        if (_undoEngine is null && TryGetService(out _undoEngine))
        {
            _undoEngine.Undoing += OnUndoing;
            _undoEngine.Undone += OnUndone;
        }
    }

    // internal since the Behavior uses this when the Items are moved on the DropDown.
    internal void InitializeBodyGlyphsForItems(bool addGlyphs /* true for add */, ToolStripDropDownItem item)
    {
        if (addGlyphs)
        {
            AddBodyGlyphs(item);
        }
        else
        {
            RemoveBodyGlyphs(item);
        }
    }

    /// <summary>
    ///  Important function that initializes the dropDown with the typeHereNode , hooks the events and then shows the dropDown.
    /// </summary>
    internal void InitializeDropDown()
    {
        ToolStrip main = GetMainToolStrip();
        // Check if the TopMostItem is on normal dropdown or on the overflow.
        ToolStripDropDown firstDropDown = GetFirstDropDown(MenuItem);
        if (firstDropDown is not null)
        {
            ToolStripItem topMostItem = firstDropDown.OwnerItem;
            if ((topMostItem is not null && topMostItem.GetCurrentParent() is ToolStripOverflow) && !main.CanOverflow)
            {
                return;
            }
        }

        if (!_initialized)
        {
            _initialized = true;
            // When the DropDown is Shared the ownerItem need not be the current MenuItem.
            // In Such a case hide the dropDown for current owner ...
            // this will bring everything to a sane state..
            if (MenuItem.DropDown.OwnerItem is ToolStripDropDownItem currentOwner && currentOwner != MenuItem)
            {
                if (_designerHost.GetDesigner(currentOwner) is ToolStripMenuItemDesigner ownerdesigner)
                {
                    ownerdesigner.RemoveTypeHereNode(currentOwner);
                }

                currentOwner.HideDropDown();
            }

            // Check if this is a Sited-DropDown
            if (MenuItem.DropDown.Site is not null)
            {
                if (_designerHost.GetDesigner(MenuItem.DropDown) is ToolStripDropDownDesigner designer)
                {
                    designer._currentParent = MenuItem as ToolStripMenuItem;
                }
            }

            CreatetypeHereNode();
            MenuItem.DropDown.TopLevel = false;

            // Allow Drag - Drop....
            MenuItem.DropDown.AllowDrop = true;
            HookEvents();
            MenuItem.DropDown.AutoClose = false;
            MenuItem.ShowDropDown();
            ShowOwnerDropDown(MenuItem);

            // Every time you initialize the dropDown Reset Glyphs
            ResetGlyphs(MenuItem);

            if (!IsOnContextMenu && !_dummyItemAdded)
            {
                // Required to show the SelectionBorder when the item is selected through the PropertyGrid or Doc outline.
                GetService<SelectionManager>()?.Refresh();
            }

            GetService<BehaviorService>()?.Invalidate(MenuItem.Owner.Bounds);
        }
    }

    private bool IsParentDropDown(ToolStripDropDown currentDropDown)
    {
        if (currentDropDown is not null)
        {
            ToolStripDropDown startDropDown = MenuItem.Owner as ToolStripDropDown;
            while (startDropDown is not null && startDropDown != currentDropDown)
            {
                if (startDropDown.OwnerItem is ToolStripDropDownItem ownerItem)
                {
                    startDropDown = ownerItem.Owner as ToolStripDropDown;
                }
                else
                {
                    startDropDown = null;
                }
            }

            if (startDropDown is null)
            {
                return false;
            }

            return true;
        }

        return false;
    }

    /// <summary>
    ///  This will morph the current item to the provided type "t" of the item...
    /// </summary>
    internal override ToolStripItem MorphCurrentItem(Type t)
    {
        // Get the Hosting DropDown'd bounds
        Rectangle hostingDropDownBounds = (MenuItem.GetCurrentParent()).Bounds;
        // Get the currentItems DropDownBounds
        Rectangle itemDropDownBounds = MenuItem.DropDown.Bounds;
        // Remove body Glyphs...
        InitializeBodyGlyphsForItems(false /*remove*/, MenuItem);
        Rectangle boundstoInvalidate = Rectangle.Union(hostingDropDownBounds, itemDropDownBounds);
        ToolStripAdornerWindowService toolStripService = _toolStripAdornerWindowService;
        ToolStripItem newItem = base.MorphCurrentItem(t);
        // We loose the ToolStripWindowService after Morphing... so use the cached one.
        toolStripService?.Invalidate(boundstoInvalidate);

        return newItem;
    }

    /// <summary>
    ///  Fired after a component has been added. Here, we add it to the ToolStrip and select it.
    /// </summary>
    private void ComponentChangeSvc_ComponentAdded(object sender, ComponentEventArgs e)
    {
        IComponentChangeService changeService = GetService<IComponentChangeService>();

        // make sure it's one of ours and on DropDown.
        if (e.Component is ToolStripItem newItem && _componentAddingFired && (MenuItemSelected || _fireComponentChanged))
        {
            _componentAddingFired = false;
            try
            {
                if (IsOnContextMenu && MenuItem.DropDown.Site is not null)
                {
                    if (changeService is not null)
                    {
                        MemberDescriptor member = TypeDescriptor.GetProperties(MenuItem.DropDown)["Items"];
                        changeService.OnComponentChanging(MenuItem.DropDown, member);
                    }
                }
                else
                {
                    RaiseComponentChanging(TypeDescriptor.GetProperties(MenuItem)["DropDownItems"]);
                }

                // Get the current count of ToolStripItems.
                int count = MenuItem.DropDownItems.Count;

                // In Cut / Copy and Paste the 'indexToInsertNewItem' is not Set and hence is -1... so check for that value...
                if (_indexToInsertNewItem != -1)
                {
                    if (IsOnContextMenu && MenuItem.DropDown.Site is not null)
                    {
                        MenuItem.DropDown.Items.Insert(_indexToInsertNewItem, newItem);
                    }
                    else
                    {
                        MenuItem.DropDownItems.Insert(_indexToInsertNewItem, newItem);
                    }
                }
                else
                {
                    // Insert Item at the current Selection...
                    if (_selectionService.PrimarySelection is ToolStripItem selectedItem && selectedItem != MenuItem)
                    {
                        int index = MenuItem.DropDownItems.IndexOf(selectedItem);
                        if (MenuItem.DropDownDirection is ToolStripDropDownDirection.AboveLeft or ToolStripDropDownDirection.AboveRight)
                        {
                            // Add at the next Index in case of "Up-Directed" dropDown.
                            if (IsOnContextMenu && MenuItem.DropDown.Site is not null)
                            {
                                MenuItem.DropDown.Items.Insert(index + 1, newItem);
                            }
                            else
                            {
                                MenuItem.DropDownItems.Insert(index + 1, newItem);
                            }
                        }
                        else
                        {
                            if (IsOnContextMenu && MenuItem.DropDown.Site is not null)
                            {
                                MenuItem.DropDown.Items.Insert(index, newItem);
                            }
                            else
                            {
                                MenuItem.DropDownItems.Insert(index, newItem);
                            }
                        }
                    }
                    else
                    {
                        if (count > 0)
                        {
                            if (MenuItem.DropDownDirection is not ToolStripDropDownDirection.AboveLeft and not ToolStripDropDownDirection.AboveRight)
                            {
                                // ADD at Last but one, the last one being the TemplateNode always...
                                if (IsOnContextMenu && MenuItem.DropDown.Site is not null)
                                {
                                    MenuItem.DropDown.Items.Insert(count - 1, newItem);
                                }
                                else
                                {
                                    MenuItem.DropDownItems.Insert(count - 1, newItem);
                                }
                            }
                        }
                        else // count == 0
                        {
                            if (IsOnContextMenu && MenuItem.DropDown.Site is not null)
                            {
                                MenuItem.DropDown.Items.Add(newItem);
                            }
                            else
                            {
                                MenuItem.DropDownItems.Add(newItem);
                            }
                        }
                    }
                }

                // If the Item is added through Undo/Redo ONLY then select the item
                if (_undoingCalled)
                {
                    _selectionService?.SetSelectedComponents(new IComponent[] { newItem }, SelectionTypes.Replace);
                }

                ResetGlyphs(MenuItem);
            }
            catch
            {
                CommitInsertTransaction(commit: false);
            }
            finally
            {
                if (IsOnContextMenu && MenuItem.DropDown.Site is not null)
                {
                    if (changeService is not null)
                    {
                        MemberDescriptor member = TypeDescriptor.GetProperties(MenuItem.DropDown)["Items"];
                        changeService.OnComponentChanged(MenuItem.DropDown, member);
                    }
                }
                else
                {
                    RaiseComponentChanged(TypeDescriptor.GetProperties(MenuItem)["DropDownItems"], null, null);
                }

                CommitInsertTransaction(/*commit=*/true);
            }
        }
    }

    private void CommitInsertTransaction(bool commit)
    {
        if (!IsOnContextMenu)
        {
            ToolStrip mainStrip = GetMainToolStrip();
            if (_designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner && mainStripDesigner.InsertTransaction is not null)
            {
                if (commit)
                {
                    mainStripDesigner.InsertTransaction.Commit();
                }
                else
                {
                    mainStripDesigner.InsertTransaction.Cancel();
                }

                mainStripDesigner.InsertTransaction = null;
            }
        }
        else
        {
            if (_insertMenuItemTransaction is not null)
            {
                if (commit)
                {
                    _insertMenuItemTransaction.Commit();
                }
                else
                {
                    _insertMenuItemTransaction.Cancel();
                }

                _insertMenuItemTransaction = null;
            }
        }
    }

    /// <summary>
    ///  Checks if the component being added is a child ToolStripItem.
    /// </summary>
    private void ComponentChangeSvc_ComponentAdding(object sender, ComponentEventArgs e)
    {
        // Don't do anything if CopyInProgress is true
        if (KeyboardHandlingService is not null && KeyboardHandlingService.CopyInProgress)
        {
            return;
        }

        if (e.Component is ToolStripItem && (MenuItemSelected || _fireComponentChanged))
        {
            if (!IsOnContextMenu)
            {
                ToolStrip mainStrip = GetMainToolStrip();
                if (_designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner && !mainStripDesigner.EditingCollection && mainStripDesigner.InsertTransaction is null)
                {
                    _componentAddingFired = true;
                    Debug.Assert(_designerHost is not null, "Why didn't we get a designer host?");
                    mainStripDesigner.InsertTransaction = _designerHost.CreateTransaction(SR.ToolStripInsertingIntoDropDownTransaction);
                }
            }
            else  // we are on ContextMenuStrip, ToolStripDropDown or ToolStripDropDownMenu....
            {
                if (e.Component is ToolStripItem itemAdding && itemAdding.Owner is null)
                {
                    _componentAddingFired = true;
                    Debug.Assert(_designerHost is not null, "Why didn't we get a designer host?");
                    _insertMenuItemTransaction = _designerHost.CreateTransaction(SR.ToolStripInsertingIntoDropDownTransaction);
                }
            }
        }
    }

    /// <summary>
    ///  After a ToolStripItem is removed, remove it from the ToolStrip and select the next item.
    /// </summary>
    private void ComponentChangeSvc_ComponentRemoved(object sender, ComponentEventArgs e)
    {
        if (e.Component is ToolStripItem itemToBeDeleted && itemToBeDeleted.IsOnDropDown)
        {
            if (itemToBeDeleted.Owner is ToolStripDropDown owner)
            {
                // Get the ownerItem
                ToolStripDropDownItem ownerItem = (ToolStripDropDownItem)owner.OwnerItem;
                if (ownerItem is not null && ownerItem == MenuItem)
                {
                    int itemIndex = ownerItem.DropDownItems.IndexOf(itemToBeDeleted);
                    // send notifications.
                    try
                    {
                        if (itemIndex != -1)
                        {
                            ownerItem.DropDownItems.Remove(itemToBeDeleted);
                            RaiseComponentChanged(TypeDescriptor.GetProperties(ownerItem)["DropDownItems"], null, null);
                        }
                    }
                    finally
                    {
                        _pendingTransaction?.Commit();
                        _pendingTransaction = null;
                    }

                    // Remove & Add the Glyphs
                    ResetGlyphs(ownerItem);
                    // select the next item or the ToolStrip itself.
                    if (ownerItem.DropDownItems.Count > 1)
                    {
                        itemIndex = Math.Min(ownerItem.DropDownItems.Count - 1, itemIndex);
                        itemIndex = Math.Max(0, itemIndex);
                    }
                    else
                    {
                        itemIndex = -1;
                    }

                    // Looks like we need to invalidate the entire
                    if (_toolStripAdornerWindowService is not null && _boundsToInvalidateOnRemove != Rectangle.Empty)
                    {
                        using Region regionToInvalidate = new(_boundsToInvalidateOnRemove);
                        regionToInvalidate.Exclude(MenuItem.DropDown.Bounds);
                        _toolStripAdornerWindowService.Invalidate(regionToInvalidate);
                        _boundsToInvalidateOnRemove = Rectangle.Empty;
                    }

                    // Select the item only if Cut/Delete is pressed.
                    if (KeyboardHandlingService is not null && KeyboardHandlingService.CutOrDeleteInProgress)
                    {
                        if (_selectionService is not null && !_dummyItemAdded)
                        {
                            IComponent targetSelection = (itemIndex == -1) ? ownerItem : ownerItem.DropDownItems[itemIndex];
                            // if the TemplateNode becomes the targetSelection, then set the targetSelection to null.
                            if (targetSelection is DesignerToolStripControlHost)
                            {
                                KeyboardHandlingService.SelectedDesignerControl = targetSelection;
                                KeyboardHandlingService.OwnerItemAfterCut = MenuItem;
                                _selectionService.SetSelectedComponents(null, SelectionTypes.Replace);
                            }
                            else
                            {
                                _selectionService.SetSelectedComponents(new IComponent[] { targetSelection }, SelectionTypes.Replace);
                            }
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    ///  Before a ToolStripItem is removed, open a transaction to batch the operation.
    /// </summary>
    private void ComponentChangeSvc_ComponentRemoving(object sender, ComponentEventArgs e)
    {
        if (_dummyItemAdded)
        {
            return;
        }

        if (e.Component is ToolStripItem itemToBeDeleted && itemToBeDeleted.IsOnDropDown && itemToBeDeleted.Placement == ToolStripItemPlacement.Main)
        {
            if (itemToBeDeleted.Owner is ToolStripDropDown owner)
            {
                // Get the ownerItem
                ToolStripDropDownItem ownerItem = (ToolStripDropDownItem)owner.OwnerItem;
                if (ownerItem is not null && ownerItem == MenuItem)
                {
                    RemoveItemBodyGlyph(itemToBeDeleted);
                    InitializeBodyGlyphsForItems(false, ownerItem);
                    _boundsToInvalidateOnRemove = ownerItem.DropDown.Bounds;
                    // Check if the deleted item is a dropDownItem and its DropDown is Visible.
                    if (itemToBeDeleted is ToolStripDropDownItem dropDownItem)
                    {
                        _boundsToInvalidateOnRemove = Rectangle.Union(_boundsToInvalidateOnRemove, dropDownItem.DropDown.Bounds);
                    }

                    Debug.Assert(_designerHost is not null, "Why didn't we get a designer host?");
                    Debug.Assert(_pendingTransaction is null, "Adding item with pending transaction?");
                    try
                    {
                        _pendingTransaction = _designerHost.CreateTransaction(SR.ToolStripDesignerTransactionRemovingItem);
                        RaiseComponentChanging(TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]);
                    }
                    catch
                    {
                        _pendingTransaction?.Cancel();
                        _pendingTransaction = null;
                    }
                }
            }
        }
    }

    /// <summary>
    ///  Controls the dismissal of the drop down, here - we just cancel it
    /// </summary>
    private void OnDropDownClosing(object sender, ToolStripDropDownClosingEventArgs e)
    {
        // always dismiss this so we don't collapse the dropdown when the user clicks @ design time
        e.Cancel = (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked);
    }

    /// <summary>
    ///  When DropDown is disposed; nullify the dropDown.
    /// </summary>
    private void OnDropDownDisposed(object sender, EventArgs e)
    {
        if (MenuItem is not null)
        {
            if (MenuItem.DropDown is not null)
            {
                MenuItem.DropDown.Disposed -= OnDropDownDisposed;
            }

            // This is necessary when the MenuItem's DropDown property is SET to something other than the default DropDown.
            MenuItem.DropDown = null;
        }
    }

    /// <summary>
    ///  When a item is added, re-arrange the elements to make sure that the templateNode is at the end..
    /// </summary>
    private void OnItemAdded(object sender, ToolStripItemEventArgs e)
    {
        // Reshuffle the TemplateNode only for "Downward" dropdowns.
        if (MenuItem.DropDownDirection is not ToolStripDropDownDirection.AboveLeft and not ToolStripDropDownDirection.AboveRight)
        {
            if (_typeHereNode is not null && (e.Item != _typeHereNode))
            {
                int currentIndexOfEditor = MenuItem.DropDown.Items.IndexOf(_typeHereNode);
                if (currentIndexOfEditor >= 0 && currentIndexOfEditor < MenuItem.DropDown.Items.Count - 1)
                {
                    // we now know the editor is there, but isn't currently at the end of the line. lets add it.
                    MenuItem.DropDown.ItemAdded -= OnItemAdded;
                    MenuItem.DropDown.SuspendLayout();
                    MenuItem.DropDown.Items.Remove(_typeHereNode);
                    MenuItem.DropDown.Items.Add(_typeHereNode);
                    MenuItem.DropDown.ResumeLayout();
                    MenuItem.DropDown.ItemAdded += OnItemAdded;
                }
                else
                {
                    CreatetypeHereNode();
                }
            }
        }
    }

    /// <summary>
    ///  Called during Undo (this is used for DropDown Property)
    /// </summary>
    private void OnUndone(object source, EventArgs e)
    {
        if (_undoingCalled)
        {
            // If we have Undone the SETTING of DropDown Revert back to Original state.
            if (_dropDownSet && MenuItem.DropDown.IsAutoGenerated)
            {
                ToolStrip mainStrip = GetMainToolStrip();
                if (_designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner && mainStripDesigner.CacheItems)
                {
                    foreach (ToolStripItem item in mainStripDesigner.Items)
                    {
                        MenuItem.DropDownItems.Insert(0, item);
                    }

                    mainStripDesigner.CacheItems = false;
                }

                ResetGlyphs(MenuItem);
            }

            // since the dropDown is closed during UnDoing .. we need to re-open the dropDown in Undone.
            if (MenuItem is not null && _selectionService.GetComponentSelected(MenuItem))
            {
                InitializeDropDown();
                MenuItem.DropDown.PerformLayout();
            }

            _undoingCalled = false;
            _dropDownSet = false;
        }

        // After Redo-Undo Glyphs are broken.
        if (_selectionService.GetComponentSelected(MenuItem) && !_dropDownSetFailed)
        {
            InitializeDropDown();
        }
    }

    /// <summary>
    ///  Called during Undo (this is used for DropDown Property)
    /// </summary>
    private void OnUndoing(object source, EventArgs e)
    {
        if (_dummyItemAdded)
        {
            return;
        }

        if (!IsOnContextMenu && MenuItem.DropDown.Visible)
        {
            MenuItem.HideDropDown();

            if (!MenuItem.DropDown.IsAutoGenerated)
            {
                _dropDownSet = true;
                ToolStrip mainStrip = GetMainToolStrip();
                if (_designerHost.GetDesigner(mainStrip) is ToolStripDesigner mainStripDesigner)
                {
                    mainStripDesigner.CacheItems = true;
                    mainStripDesigner.Items.Clear();
                }
            }

            _undoingCalled = true;
        }
    }

    /// <summary>
    ///  Once a menuItem designer has selection - be sure to expand and collapse all necessary child/parent items
    ///  Implements the Selection Paint Logic by adding Text to Tag property. Also Hides Unnecessary DropDowns.
    /// </summary>
    private void OnSelectionChanged(object sender, EventArgs e)
    {
        // determine if we are selected
        if (MenuItem is null)
        {
            return;
        }

        ISelectionService selectionSvc = sender as ISelectionService;
        Debug.Assert(selectionSvc is not null, "No Selection Service !!");
        if (selectionSvc is null)
        {
            return;
        }

        // ALWAYS COMMIT!!!
        if (_commitedTemplateNode is not null && _commitedTemplateNode.Active)
        {
            _commitedTemplateNode.Commit(false, false);
        }
        else if (_typeHereTemplateNode is not null && _typeHereTemplateNode.Active)
        {
            _typeHereTemplateNode.Commit(false, false);
        }

        if (MenuItem.Equals(selectionSvc.PrimarySelection))
        {
            ArrayList origSel = ToolStripDesignerUtils.s_originalSelComps;
            if (origSel is not null)
            {
                ToolStripDesignerUtils.InvalidateSelection(origSel, MenuItem, MenuItem.Site, false /*shift pressed*/);
            }

            if (IsOnContextMenu && !MenuItem.Owner.Visible)
            {
                ToolStripDropDown firstDropDown = GetFirstDropDown(MenuItem);
                if (_designerHost.GetDesigner(firstDropDown) is ToolStripDropDownDesigner firstDropDownDesigner)
                {
                    InitializeDropDown();
                    firstDropDownDesigner.ShowMenu();
                    firstDropDownDesigner.AddSelectionGlyphs();
                }
            }
            else
            {
                InitializeDropDown();
            }

            // Cache original selection
            ICollection originalSelComps = null;
            if (_selectionService is not null)
            {
                originalSelComps = selectionSvc.GetSelectedComponents();
            }

            // Add the TemplateNode to the Selection if it is currently Selected as the GetSelectedComponents won't do it for us.
            origSel = new ArrayList(originalSelComps);
            if (origSel.Count == 0)
            {
                if (KeyboardHandlingService is not null && KeyboardHandlingService.SelectedDesignerControl is not null)
                {
                    origSel.Add(KeyboardHandlingService.SelectedDesignerControl);
                }
            }

            if (origSel.Count > 0)
            {
                ToolStripDesignerUtils.s_originalSelComps = origSel;
            }
        }
        else
        {
            object selectedObj = ((ISelectionService)sender).PrimarySelection;
            if (selectedObj is null)
            {
                if (KeyboardHandlingService is not null)
                {
                    selectedObj = KeyboardHandlingService.SelectedDesignerControl;
                }
            }

            if (selectedObj is ToolStripItem currentSelection)
            {
                ToolStripDropDown parent = currentSelection.Owner as ToolStripDropDown;
                while (parent is not null)
                {
                    if (parent.OwnerItem == MenuItem || parent.OwnerItem is null)
                    {
                        return;
                    }
                    else
                    {
                        parent = parent.OwnerItem.Owner as ToolStripDropDown;
                    }
                }
            }

            if (MenuItem.DropDown.Visible)
            {
                // CASE : Check if the DropDown Selected is the one assigned to this MenuItem's DropDown property.
                // If MenuItem.DropDown matches the currentSelection or is the First Dropdown
                // of any selection THEN return
                if (selectedObj is ToolStripDropDown selectedDropDown && MenuItem.DropDown == selectedDropDown)
                {
                    return;
                }
                else
                {
                    // Any ToolStripItem on the DropDown OR any of its Child DropDowns
                    if (selectedObj is ToolStripItem item)
                    {
                        ToolStripDropDown parent = item.Owner as ToolStripDropDown;
                        while (parent is not null)
                        {
                            if (parent == MenuItem.DropDown)
                            {
                                return;
                            }
                            else
                            {
                                parent = parent.OwnerItem.Owner as ToolStripDropDown;
                            }
                        }
                    }
                }

                // Close your own dropDown...
                if (MenuItem.DropDown.OwnerItem == MenuItem)
                {
                    MenuItem.HideDropDown();
                }
            }
        }
    }

    /// <summary>
    ///  Allows a designer to filter the set of properties the component it is designing will expose through
    ///  the TypeDescriptor object. This method is called immediately before its corresponding "Post" method.
    ///  If you are overriding this method you should call the base implementation before you perform your own filtering.
    /// </summary>
    protected override void PreFilterProperties(IDictionary properties)
    {
        base.PreFilterProperties(properties);
        // Handle shadowed properties
        string[] shadowProps = ["Visible", "DoubleClickEnabled", "CheckOnClick", "DropDown"];
        PropertyDescriptor prop;
        Attribute[] empty = [];
        for (int i = 0; i < shadowProps.Length; i++)
        {
            prop = (PropertyDescriptor)properties[shadowProps[i]];
            if (prop is not null)
            {
                properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(ToolStripMenuItemDesigner), prop, empty);
            }
        }
    }

    /// <summary>
    ///  Resets the ToolStripMenuItem DoubleClickEnabled to be the default visible
    /// </summary>
    private void ResetDoubleClickEnabled() => DoubleClickEnabled = false;

    /// <summary>
    ///  Resets the ToolStripMenuItem CheckOnClick to be the default visible
    /// </summary>
    private void ResetCheckOnClick() => CheckOnClick = false;

    /// <summary>
    ///  Resets the ToolStripMenuItem CheckOnClick to be the default visible
    /// </summary>
    private void ResetDropDown() => DropDown = null;

    /// <summary>
    ///  Resets the ToolStripMenuItem Visible to be the default visible
    /// </summary>
    private void ResetVisible() => Visible = true;

    /// <summary>
    ///  Restores the ToolStripMenuItem Visible to be the value set in the property grid.
    /// </summary>
    private void RestoreVisible() => MenuItem.Visible = Visible;

    /// <summary>
    ///  Removes the TypeHere node when the DropDownCloses.
    /// </summary>
    internal void RemoveTypeHereNode(ToolStripDropDownItem ownerItem)
    {
        // This will cause the DropDown to Relayout so that during the DropDownClosed event we won't have
        // proper Bounds to Invalidate the ToolStripAdorner... So for this case do it here...
        Rectangle bounds = ownerItem.DropDown.Bounds;
        if (ownerItem.DropDownItems.Count > 0 && ownerItem.DropDownItems[0] is DesignerToolStripControlHost)
        {
            ownerItem.DropDownItems.RemoveAt(0);
        }

        if (_typeHereTemplateNode is not null && _typeHereTemplateNode.Active)
        {
            _typeHereTemplateNode.RollBack();
            _typeHereTemplateNode.CloseEditor();
            _typeHereTemplateNode = null;
        }

        _typeHereNode?.Dispose();
        _typeHereNode = null;

        _toolStripAdornerWindowService?.Invalidate(bounds);
    }

    /// <summary>
    ///  This private function is called to ROLLBACK the current InSitu editing mode.
    /// </summary>
    private void RollBack()
    {
        if (_commitedEditorNode is not null)
        {
            int index = MenuItem.DropDownItems.IndexOf(_commitedEditorNode);
            Debug.Assert(index != -1, "Invalid Index");
            ToolStripDropDownItem editedItem = (ToolStripDropDownItem)MenuItem.DropDownItems[index + 1];
            editedItem?.Visible = true;

            MenuItem.DropDown.Items.Remove(_commitedEditorNode);
            if (_commitedTemplateNode is not null)
            {
                _commitedTemplateNode.RollBack();
                _commitedTemplateNode.CloseEditor();
                _commitedTemplateNode = null;
            }

            _commitedEditorNode?.Dispose();
            _commitedEditorNode = null;
        }
    }

    /// <summary>
    ///  Remove Body glyphs when the dropDown is closed.
    /// </summary>
    private void RemoveBodyGlyphs(ToolStripDropDownItem item)
    {
        if (item is not null)
        {
            foreach (ToolStripItem ddItem in item.DropDownItems)
            {
                ToolStripItemDesigner dropDownItemDesigner = (ToolStripItemDesigner)_designerHost.GetDesigner(ddItem);
                if (dropDownItemDesigner is not null)
                {
                    ControlBodyGlyph glyph = dropDownItemDesigner._bodyGlyph;
                    if (glyph is not null && _toolStripAdornerWindowService is not null && _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(glyph))
                    {
                        _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(glyph);
                        dropDownItemDesigner._bodyGlyph = null;
                    }
                }
            }
        }
    }

    /// <summary>
    ///  Remove glyphs per item
    /// </summary>
    internal void RemoveItemBodyGlyph(ToolStripItem item)
    {
        if (item is not null)
        {
            ToolStripItemDesigner itemDesigner = (ToolStripItemDesigner)_designerHost.GetDesigner(item);
            if (itemDesigner is not null)
            {
                ControlBodyGlyph glyph = itemDesigner._bodyGlyph;
                if (glyph is not null && _toolStripAdornerWindowService is not null && _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Contains(glyph))
                {
                    _toolStripAdornerWindowService.DropDownAdorner.Glyphs.Remove(glyph);
                    itemDesigner._bodyGlyph = null;
                }
            }
        }
    }

    /// <summary>
    ///  Helper function to remove and then re-add the glyphs.
    /// </summary>
    internal void ResetGlyphs(ToolStripDropDownItem item)
    {
        // Reset the glyphs only if the DropDown is visible.
        if (item.DropDown.Visible)
        {
            InitializeBodyGlyphsForItems(false, item);
            InitializeBodyGlyphsForItems(true, item);
        }
    }

    /// <summary>
    ///  Set the Selection after a InSitu edit is complete.
    /// </summary>
    internal override bool SetSelection(bool enterKeyPressed)
    {
        if (enterKeyPressed)
        {
            if (!_initialized)
            {
                InitializeDropDown();
            }

            // set the selection to our new item
            if (_selectionService is not null)
            {
                if (KeyboardHandlingService is not null)
                {
                    int count = 0;
                    if (MenuItem.DropDownDirection is not ToolStripDropDownDirection.AboveLeft and not ToolStripDropDownDirection.AboveRight)
                    {
                        // index to the last item.
                        count = MenuItem.DropDownItems.Count;
                        count--;
                    }

                    _selectionService.SetSelectedComponents(new object[] { MenuItem }, SelectionTypes.Replace);
                    if (count >= 0)
                    {
                        KeyboardHandlingService.SelectedDesignerControl = MenuItem.DropDownItems[count];
                        _selectionService.SetSelectedComponents(null, SelectionTypes.Replace);
                    }
                }
            }

            return true;
        }

        return false;
    }

    /// <summary>
    ///  Returns true if the visible property should be persisted in code gen.
    /// </summary>
    private bool ShouldSerializeDoubleClickEnabled() => (bool)ShadowProperties[nameof(DoubleClickEnabled)];

    /// <summary>
    ///  Returns true if the CheckOnClick property should be persisted in code gen.
    /// </summary>
    private bool ShouldSerializeCheckOnClick() => (bool)ShadowProperties[nameof(CheckOnClick)];

    /// <summary>
    ///  Returns true if the CheckOnClick property should be persisted in code gen.
    /// </summary>
    private bool ShouldSerializeDropDown() => (_customDropDown is not null);

    /// <summary>
    ///  Returns true if the visible property should be persisted in code gen.
    /// </summary>
    private bool ShouldSerializeVisible() => !Visible;

    /// <summary>
    ///  This Function is called thru the ToolStripEditorManager which is listening for the F2 command.
    /// </summary>
    internal override void ShowEditNode(bool clicked)
    {
        if (MenuItem is null)
        {
            return;
        }

        try
        {
            if (MenuItem.Owner is ToolStripDropDown dropDown)
            {
                _parentItem = dropDown.OwnerItem;
                // need to inform the owner tha we want to enter InSitu mode
                if (_designerHost is not null)
                {
                    IDesigner designer = _designerHost.GetDesigner(_parentItem);
                    if (designer is ToolStripMenuItemDesigner menuItemDesigner)
                    {
                        menuItemDesigner.EnterInSituEdit(MenuItem);
                    }
                }
            }

            // We come here for TOP LEVEL MENUITEM .. So call base version...
            else
            {
                base.ShowEditNode(clicked);
            }
        }
        catch (CheckoutException checkoutException)
        {
            // Do not crash on canceled checkout
            if (checkoutException.Equals(CheckoutException.Canceled))
            {
                return;
            }
            else
            {
                throw;
            }
        }
    }

    /// <summary>
    ///  This Function would select all items starting form oldSelection to the Current MenuItem.
    /// </summary>
    private void SelectItems(ToolStripDropDownItem oldSelection, ISelectionService selSvc)
    {
        ToolStripDropDown ownerDropDown = (ToolStripDropDown)MenuItem.Owner;
        int maxIndex = Math.Max(ownerDropDown.Items.IndexOf(oldSelection), ownerDropDown.Items.IndexOf(MenuItem));
        int minIndex = Math.Min(ownerDropDown.Items.IndexOf(oldSelection), ownerDropDown.Items.IndexOf(MenuItem));
        ToolStripItem[] selectedItems = new ToolStripItem[maxIndex - minIndex + 1];
        int i = 0;
        while (minIndex <= maxIndex)
        {
            selectedItems[i] = ownerDropDown.Items[minIndex];
            i++;
            minIndex++;
        }

        selSvc.SetSelectedComponents(selectedItems);
    }

    /// <summary>
    ///  Shows ALL the owner DropDowns if passed in MenuItem is Selected
    /// </summary>
    internal void ShowOwnerDropDown(ToolStripDropDownItem currentSelection)
    {
        // We MIGHT HAVE TO START TOP - DOWN Instead of BOTTOM-UP.
        // Sometimes we DON'T get the Owner POPUP and hence all the popup are parented to Wrong guy.
        while (currentSelection is not null && currentSelection.Owner is ToolStripDropDown dropDown)
        {
            ToolStripDropDown currentDropDown = dropDown;
            currentSelection = (ToolStripDropDownItem)currentDropDown.OwnerItem;
            if (currentSelection is not null && !currentSelection.DropDown.Visible)
            {
                if (_designerHost.GetDesigner(currentSelection) is ToolStripMenuItemDesigner currentSelectionDesigner)
                {
                    currentSelectionDesigner.InitializeDropDown();
                }
            }
            else if (currentDropDown is ContextMenuStrip && !currentDropDown.Visible)
            {
                // ContextMenuStrip does not have an owner item, and need be shown with different method
                ToolStripDropDownDesigner dropDownDesigner = (ToolStripDropDownDesigner)_designerHost.GetDesigner(currentDropDown);
                dropDownDesigner?.ShowMenu(null);
            }
        }
    }

    internal void UnHookEvents()
    {
        if (MenuItem is not null)
        {
            MenuItem.DropDown.Closing -= OnDropDownClosing;
            MenuItem.DropDownOpening -= DropDownItem_DropDownOpening;
            MenuItem.DropDownOpened -= DropDownItem_DropDownOpened;
            MenuItem.DropDownClosed -= DropDownItem_DropDownClosed;
            MenuItem.DropDown.Resize -= DropDownResize;
            MenuItem.DropDown.ItemAdded -= OnItemAdded;
            MenuItem.DropDown.Paint -= DropDownPaint;
            MenuItem.DropDown.LocationChanged -= DropDownLocationChanged;
            MenuItem.DropDown.Click -= DropDownClick;
        }
    }

    /// <summary>
    ///  The glyph we put over the items. Basically this sets the hit-testable area of the item itself.
    /// </summary>
    internal class ToolStripDropDownGlyph : Glyph
    {
        private Rectangle _bounds;
        internal ToolStripDropDownGlyph(Rectangle bounds, Behavior.Behavior b) : base(b)
        {
            _bounds = bounds;
        }

        /// <summary>
        ///  Abstract method that forces Glyph implementations to provide hit test logic.
        ///  Given any point - if the Glyph has decided to be involved with that location,
        ///  the Glyph will need to return a valid Cursor. Otherwise, returning null will cause
        ///  the BehaviorService to simply ignore it.
        /// </summary>
        public override Cursor GetHitTest(Point p)
        {
            if (_bounds.Contains(p))
            {
                return Cursors.Default;
            }

            return null;
        }

        /// <summary>
        ///  Overrides Glyph::Paint - this implementation does nothing.
        /// </summary>
        public override void Paint(PaintEventArgs pe)
        {
        }
    }

    /// <summary>
    ///  The transparent behavior on top of the DropDownGlyphs.
    /// </summary>
    internal class DropDownBehavior : ControlDesigner.TransparentBehavior
    {
        /// <summary>
        ///  Constructor that accepts the related ControlDesigner.
        /// </summary>
        private readonly ToolStripMenuItemDesigner _menuItemDesigner;

        internal DropDownBehavior(ControlDesigner designer, ToolStripMenuItemDesigner menuItemDesigner) : base(designer)
        {
            _menuItemDesigner = menuItemDesigner;
        }

        /// <summary>
        ///  Drag drop support on the DropDown...
        /// </summary>
        public override void OnDragEnter(Glyph g, DragEventArgs e)
        {
            if (e.Data is ToolStripItemDataObject)
            {
                e.Effect = (Control.ModifierKeys == Keys.Control) ? DragDropEffects.Copy : DragDropEffects.Move;
            }
            else
            {
                base.OnDragEnter(g, e);
            }
        }

        /// <summary>
        ///  Drag drop support on the DropDown...
        /// </summary>
        public override void OnDragOver(Glyph g, DragEventArgs e)
        {
            if (e.Data is ToolStripItemDataObject)
            {
                e.Effect = (Control.ModifierKeys == Keys.Control) ? DragDropEffects.Copy : DragDropEffects.Move;
            }
            else
            {
                base.OnDragOver(g, e);
            }
        }

        /// <summary>
        ///  Drag drop support on the DropDown...
        /// </summary>
        public override void OnDragDrop(Glyph g, DragEventArgs e)
        {
            if (e.Data is ToolStripItemDataObject data)
            {
                ToolStripItem primaryItem = data.PrimarySelection;
                IDesignerHost host = (IDesignerHost)primaryItem.Site.GetService(typeof(IDesignerHost));
                ToolStripDropDown parentToolStrip = primaryItem.GetCurrentParent() as ToolStripDropDown;
                ToolStripDropDownItem ownerItem = null;
                if (parentToolStrip is not null)
                {
                    ownerItem = parentToolStrip.OwnerItem as ToolStripDropDownItem;
                }

                Debug.Assert(ownerItem is not null, "How can ownerItem be null for a menu item on a dropdown?");
                if (ownerItem is not null && host is not null)
                {
                    string transDesc;
                    List<ToolStripItem> dragComponents = data.DragComponents;
                    int primaryIndex = -1;
                    bool copy = (e.Effect == DragDropEffects.Copy);
                    if (dragComponents.Count == 1)
                    {
                        string name = TypeDescriptor.GetComponentName(dragComponents[0]);
                        if (name is null || name.Length == 0)
                        {
                            name = dragComponents[0].GetType().Name;
                        }

                        transDesc = string.Format(copy ? SR.BehaviorServiceCopyControl : SR.BehaviorServiceMoveControl, name);
                    }
                    else
                    {
                        transDesc = string.Format(copy ? SR.BehaviorServiceCopyControls : SR.BehaviorServiceMoveControls, dragComponents.Count);
                    }

                    // create a transaction so this happens as an atomic unit.
                    DesignerTransaction changeParent = host.CreateTransaction(transDesc);
                    try
                    {
                        var changeService = primaryItem.Site.GetService<IComponentChangeService>();
                        changeService?.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]);

                        IReadOnlyList<IComponent> components;

                        // If we are copying, then we want to make a copy of the components we are dragging
                        if (copy)
                        {
                            // Remember the primary selection if we had one
                            if (primaryItem is not null)
                            {
                                primaryIndex = dragComponents.IndexOf(primaryItem);
                            }

                            ToolStripKeyboardHandlingService keyboardHandlingService = (ToolStripKeyboardHandlingService)primaryItem.Site.GetService(typeof(ToolStripKeyboardHandlingService));
                            keyboardHandlingService?.CopyInProgress = true;

                            components = DesignerUtils.CopyDragObjects(dragComponents, primaryItem.Site);

                            keyboardHandlingService?.CopyInProgress = false;

                            if (primaryIndex != -1)
                            {
                                primaryItem = components[primaryIndex] as ToolStripItem;
                            }
                        }
                        else
                        {
                            components = dragComponents;
                        }

                        if (e.Effect == DragDropEffects.Move || copy)
                        {
                            // Add the item.
                            foreach (ToolStripItem toolItem in components)
                            {
                                parentToolStrip.Items.Add(toolItem);
                            }

                            // Open the DropDown for the PrimarySelection before the DRAG-DROP operation.
                            if (primaryItem is ToolStripDropDownItem dropDownItem)
                            {
                                if (host.GetDesigner(dropDownItem) is ToolStripMenuItemDesigner dropDownItemDesigner)
                                {
                                    dropDownItemDesigner.InitializeDropDown();
                                }
                            }

                            // Set the Selection ..
                            _menuItemDesigner._selectionService.SetSelectedComponents(new IComponent[] { primaryItem }, SelectionTypes.Primary | SelectionTypes.Replace);
                        }

                        changeService?.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]);

                        // fire extra changing/changed events so that the order is "restored" after undo/redo
                        if (copy)
                        {
                            if (changeService is not null)
                            {
                                changeService.OnComponentChanging(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]);
                                changeService.OnComponentChanged(ownerItem, TypeDescriptor.GetProperties(ownerItem)["DropDownItems"]);
                            }
                        }

                        // If Parent is DropDown... we have to manage the Glyphs ....
                        if (ownerItem is not null)
                        {
                            if (host.GetDesigner(ownerItem) is ToolStripMenuItemDesigner ownerDesigner)
                            {
                                ownerDesigner.InitializeBodyGlyphsForItems(false, ownerItem);
                                ownerDesigner.InitializeBodyGlyphsForItems(true, ownerItem);
                            }
                        }

                        // Refresh the BehaviorService.
                        BehaviorService bSvc = (BehaviorService)primaryItem.Site.GetService(typeof(BehaviorService));
                        bSvc?.SyncSelection();
                    }
                    catch
                    {
                        changeParent?.Cancel();
                        changeParent = null;
                    }
                    finally
                    {
                        changeParent?.Commit();
                    }
                }
            }
        }
    }
}
